package qdb

import (
	"bytes"
	"context"
	"encoding/json"
	"gfrest/common/util"
	"log"
	"strings"
	"time"

	"github.com/elastic/go-elasticsearch/esapi"
	"github.com/gogf/gf/frame/g"
	"github.com/joeguo/tldextract"
)

func getDomain(urlstr string) string {
	cache := "/tmp/tld.cache"
	extract, _ := tldextract.New(cache, false)

	result := extract.Extract(urlstr)
	g.Log().Printf("%+v;%s\n", result, urlstr)
	return result.Root + "." + result.Tld
}

func getFirstScrollId() ([]*EsIcpInfo, string) {
	var buf bytes.Buffer
	query := map[string]interface{}{}
	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		g.Log().Errorf("getFirstScrollId Error encoding query: %s", err)
	}

	// Perform the search request.
	res, err := EsClient.Search(
		EsClient.Search.WithContext(context.Background()),
		EsClient.Search.WithIndex("test"),
		EsClient.Search.WithBody(&buf),
		EsClient.Search.WithTrackTotalHits(true),
		EsClient.Search.WithPretty(),
		EsClient.Search.WithScroll(time.Second*86400),
		EsClient.Search.WithSize(1000),
	)

	if err != nil {
		g.Log().Errorf("Error getting response: %s", err)
	}
	defer res.Body.Close()
	if res.IsError() {
		var e map[string]interface{}
		if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
			g.Log().Errorf("getFirstScrollId Error parsing the response body: %s", err)
		} else {
			// Print the response status and error information.
			g.Log().Info(e)
			g.Log().Errorf(" getFirstScrollId [%s] %s: %s",
				res.Status(),
				e["error"].(map[string]interface{})["type"],
				e["error"].(map[string]interface{})["reason"],
			)
		}
	}
	var ret = []*EsIcpInfo{}

	r := EsResponse{}
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		g.Log().Errorf("Error parsing the response body: %s", err)
		return ret, ""
	}
	g.Log().Infof("getFirstScrollId r.ScrollId: %v", r.ScrollId)

	for ii := range r.Hits.Hits {
		ret = append(ret, &r.Hits.Hits[ii].Source)
	}
	return ret, r.ScrollId
}

// ScrollDomain ScrollDomain
func ScrollDomain() (ret []*EsIcpInfo) {
	ScrollDomainKey := g.Config().GetString("ScrollDomainKey", "SCROLL_DOMAIN_KEY2")
	cacheVal, err := util.Cache.Get(ScrollDomainKey)
	if err != nil {
		g.Log().Error("util.Cache.get", err)
		return
	}
	g.Log().Info("cacheVal", cacheVal)

	scrollID := ""
	if cacheVal == nil || cacheVal.(string) == "" {
		ret, scrollID = getFirstScrollId()
		err = util.Cache.Set(ScrollDomainKey, scrollID, time.Second*86400)
		if err != nil {
			g.Log().Error("util.Cache.set", err)
			return
		}
		return
	} else {
		scrollID = cacheVal.(string)
	}

	var buf bytes.Buffer
	query := map[string]interface{}{}
	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		g.Log().Errorf("SearchEsIcp Error encoding query: %s", err)
	}
	res, err := EsClient.Scroll(
		EsClient.Scroll.WithScroll(time.Second*86400),
		EsClient.Scroll.WithContext(context.TODO()),
		EsClient.Scroll.WithBody(&buf),
		EsClient.Scroll.WithScrollID(scrollID),
	)
	g.Log().Info("scrollID", scrollID)

	if err != nil {
		g.Log().Errorf("Error getting response: %s", err)
	}
	defer res.Body.Close()
	if res.IsError() {
		var e map[string]interface{}
		if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
			g.Log().Errorf("ScrollDomain Error parsing the response body: %s", err)
		} else {
			// Print the response status and error information.
			g.Log().Info(e)
			g.Log().Errorf(" ScrollDomain [%s] %s: %s",
				res.Status(),
				e["error"].(map[string]interface{})["type"],
				e["error"].(map[string]interface{})["reason"],
			)
		}
	}
	r := EsResponse{}
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		g.Log().Errorf("Error parsing the response body: %s", err)
		return ret
	}
	err = util.Cache.Set(ScrollDomainKey, scrollID, time.Second*86400)
	if err != nil {
		g.Log().Error("util.Cache.set", err)
		return
	}
	g.Log().Infof("ScrollDomain res: %v", g.Export(r.Hits.Hits))

	for ii := range r.Hits.Hits {
		ret = append(ret, &r.Hits.Hits[ii].Source)
	}
	return ret

}

// SearchEsIcp SearchEsIcp
func SearchEsIcp(Domain string) *EsIcpInfo {
	Domain = getDomain(Domain) // 确保只查 Domain
	if Domain == "." {
		g.Log().Errorf("SearchEsIcp Domain empty: %v", Domain)
		return nil
	}
	var buf bytes.Buffer
	query := map[string]interface{}{
		"query": map[string]interface{}{
			"match": map[string]interface{}{
				"domain": Domain,
			},
		},
	}
	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		g.Log().Errorf("SearchEsIcp Error encoding query: %s", err)
	}

	// Perform the search request.
	res, err := EsClient.Search(
		EsClient.Search.WithContext(context.Background()),
		EsClient.Search.WithIndex("test"),
		EsClient.Search.WithBody(&buf),
		EsClient.Search.WithTrackTotalHits(true),
		EsClient.Search.WithPretty(),
	)
	if err != nil {
		g.Log().Errorf("Error getting response: %s", err)
	}
	defer res.Body.Close()
	if res.IsError() {
		var e map[string]interface{}
		if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
			g.Log().Errorf("SearchEsIcp Error parsing the response body: %s", err)
		} else {
			// Print the response status and error information.
			g.Log().Info(e)
			g.Log().Errorf(" SearchEsIcp [%s] %s: %s",
				res.Status(),
				e["error"].(map[string]interface{})["type"],
				e["error"].(map[string]interface{})["reason"],
			)
		}
	}
	r := EsResponse{}
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		g.Log().Errorf("Error parsing the response body: %s", err)
		return nil
	}
	g.Log().Infof("SearchEsIcp res: %v", r.Hits.Hits)

	if len(r.Hits.Hits) > 0 {
		return &r.Hits.Hits[0].Source
	}
	return nil

}

// PagedEsIcp PagedEsIcp
func PagedEsIcp(page, pageSize int) (ret []*EsIcpInfo) {
	if pageSize <= 0 {
		pageSize = 10
	}
	if page <= 0 {
		page = 1
	}

	var buf bytes.Buffer
	query := map[string]interface{}{}
	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		g.Log().Errorf("SearchEsIcp Error encoding query: %s", err)
	}

	// Perform the search request.
	res, err := EsClient.Search(
		EsClient.Search.WithContext(context.Background()),
		EsClient.Search.WithIndex("test"),
		EsClient.Search.WithBody(&buf),
		EsClient.Search.WithTrackTotalHits(true),
		EsClient.Search.WithPretty(),
		EsClient.Search.WithSize(pageSize),
		EsClient.Search.WithFrom((page-1)*pageSize),
	)

	if err != nil {
		g.Log().Errorf("Error getting response: %s", err)
	}
	defer res.Body.Close()
	if res.IsError() {
		var e map[string]interface{}
		if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
			g.Log().Errorf("SearchEsIcp Error parsing the response body: %s", err)
		} else {
			// Print the response status and error information.
			g.Log().Info(e)
			g.Log().Errorf(" SearchEsIcp [%s] %s: %s",
				res.Status(),
				e["error"].(map[string]interface{})["type"],
				e["error"].(map[string]interface{})["reason"],
			)
		}
	}
	r := EsResponse{}
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		g.Log().Errorf("Error parsing the response body: %s", err)
		return ret
	}
	g.Log().Infof("SearchEsIcp res: %v", r.Hits.Hits)

	for ii := range r.Hits.Hits {
		ret = append(ret, &r.Hits.Hits[ii].Source)
	}
	return ret

}

// IndexDoc IndexDoc
func IndexDoc(title string) {

	var b strings.Builder
	b.WriteString(`{"title" : "`)
	b.WriteString(title)
	b.WriteString(`"}`)

	// Set up the request object.
	req := esapi.IndexRequest{
		Index: "test",
		// DocumentID: strconv.Itoa(i + 1),
		Body:    strings.NewReader(b.String()),
		Refresh: "true",
	}

	// Perform the request with the client.
	res, err := req.Do(context.Background(), EsClient)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer res.Body.Close()

	if res.IsError() {
		log.Printf("[%s] Error indexing document ID=%v", res.Status(), g.Export(res))
	} else {
		// Deserialize the response into a map.
		var r map[string]interface{}
		if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
			log.Printf("Error parsing the response body: %s", err)
		} else {
			// Print the response status and indexed document version.
			log.Printf("[%s] %s; version=%d", res.Status(), r["result"], int(r["_version"].(float64)))
		}
	}
}
